package org.dromara.web.CssUtil;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.XML;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.therapi.runtimejavadoc.repack.com.eclipsesource.json.JsonObject;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Unmarshaller;
import lombok.RequiredArgsConstructor;
import org.dromara.app.appParams.AppBindWithdrawalAccountParams;
import org.dromara.app.appParams.AppFuYouCallBack;
import org.dromara.app.domain.SkAppUser;
import org.dromara.app.domain.SkWithdrawalLog;
import org.dromara.app.mapper.SkAppUserMapper;
import org.dromara.app.mapper.SkWithdrawalLogMapper;
import org.dromara.app.service.impl.SkWithdrawalLogServiceImpl;
import org.dromara.web.CssUtil.payUtil.OpenAccountIn;
import org.dromara.web.CssUtil.payUtil.*;
import org.dromara.web.CssUtil.payUtil.bean.*;
import org.dromara.web.CssUtil.payUtil.bean.FuiouRsaCipher;
import org.springframework.stereotype.Service;
import org.w3c.css.sac.CSSException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RequiredArgsConstructor
@Service
public class CssTiXianServer {

    private final SkWithdrawalLogServiceImpl skWithdrawalLogServiceImpl;

    private final SkWithdrawalLogMapper skWithdrawalLogMapper;

    private final SkAppUserMapper skAppUserMapper;

    /**
     * 查询是否绑定
     */
    public Map<String,Object> selectIsCunZai(SkAppUser skAppUser) throws Exception {

        AllocateAccountQueryIn in = buildAllocateAccountQueryIn(skAppUser);
        String sign = Sign.sign(in.generateClearText(), Constants.MCHNT_PRIVATE_KEY);
        in.setSignature(sign);
        String reqWithSign = XmlConvertUtil.bean2Xml(in);
        Map<String, String> param = new HashMap<String, String>();
        param.put("req", URLEncoder.encode(reqWithSign, "GBK"));
        long a= System.currentTimeMillis();
        Map<String,Object> out = HttpUtils.postInfo(Constants.QUERY_ALLOCATE_ACCOUNT_V2_PREFIX, in);
        long b= System.currentTimeMillis();
        System.out.println(b-a);
        AllocateAccountQueryOut allocateAccountQueryOut = (AllocateAccountQueryOut)out.get("AllocateAccountQueryOut");
        boolean verifySign = Sign.verify(allocateAccountQueryOut.generateClearText().getBytes(Constants.CHARSET), Constants.FY_PUBLIC_KEY, allocateAccountQueryOut.getSignature());
        if (!verifySign) {
            System.out.println("返回报文验签失败");
            throw new Exception("验证不通过");
        }
        System.out.println("请求完毕");
        return out;
    }



    /**
     * 用户创建 创建入账方
     */
    public OpenAccountOut createTiXian(AppBindWithdrawalAccountParams params) throws Exception {

        if (params.getHuMing().isEmpty()){
            throw new RuntimeException("请填写户名");
        }

        if (params.getKaHao().isEmpty()){
            throw new RuntimeException("请填写卡号");
        }

        if (params.getZjType().isEmpty()){
            throw new RuntimeException("请填写证件类型");
        }

        if (params.getZjNum().isEmpty()){
            throw new RuntimeException("请填写证件号码");
        }

        if (params.getTxPhone().isEmpty()){
            throw new RuntimeException("请填写提现手机号码");
        }
        OpenAccountIn in = buildOpenAccount(params);
        String sign = Sign.sign(in.generateClearText(), Constants.MCHNT_PRIVATE_KEY);
        in.setSignature(sign);
        String reqWithSign = XmlConvertUtil.bean2Xml(in);
        Map<String, String> param = new HashMap<String, String>();
        param.put("req", URLEncoder.encode(reqWithSign, "GBK"));
        long a= System.currentTimeMillis();
        OpenAccountOut out = HttpUtils.post(Constants.OPEN_ACCOUNT, in, OpenAccountOut.class);
        System.out.println(JSONUtils.toJSON(out));
        long b= System.currentTimeMillis();
        System.out.println(b-a);
        boolean verifySign = Sign.verify(out.generateClearText().getBytes(Constants.CHARSET), Constants.FY_PUBLIC_KEY, out.getSignature());
        if (!verifySign) {
            System.out.println("返回报文验签失败");
            throw new RuntimeException("绑定提现账户失败");
        }
        if (!"0000".equals(out.getRespCode())){
            throw new RuntimeException(out.getRespDesc());
        }
        return out;
    }

    public static AllocateAccountQueryIn buildAllocateAccountQueryIn(SkAppUser skAppUser) throws Exception {
        AllocateAccountQueryIn in = new AllocateAccountQueryIn();
        in.setTraceNo(System.currentTimeMillis() + "");
        in.setMchntCd(Constants.MCHNT_CD);
        in.setAccountIn(skAppUser.getAccountIn());
        return in;
    }
    public static OpenAccountIn buildOpenAccount(AppBindWithdrawalAccountParams params) throws Exception {
        OpenAccountIn in = new OpenAccountIn();
        in.setTraceNo(System.currentTimeMillis() + "");
        in.setMchntCd(Constants.MCHNT_CD);
        in.setCleanType("01");
        in.setOutAcntNm(params.getHuMing());
        in.setCertTp("0");
        in.setCertNo(params.getZjNum());
        in.setChannel("");
        in.setOrganizationType("");
        in.setOutAcntNo(params.getKaHao());//银行卡
        in.setInterBankNo("");
        in.setAllocateScale(null);
        in.setProtocolType("03");
        in.setMobile(params.getTxPhone());
        in.setCheckType("2");
        in.setBusiLicValidateStart("");
        in.setBusiLicValidateEnd("");
        in.setBusiLicAddr("");
        in.setBusiLicPic("");
        in.setLegalName("");
        in.setLegalMobile("");
        in.setLegalCertTp("");
        in.setLegalCertNo("");
        in.setLegalValidateStart("");
        in.setLegalValidateEnd("");
        in.setLegalImagF("");
        in.setLegalImagB("");
        in.setContactName("");
        in.setContactCertNo("");
        in.setContactEmail("");
        return in;
    }


    /**
     * 立即提现
     */
    public void withdrawImmediately(SkAppUser params, SkWithdrawalLog log) throws Exception {
//        BatchTradeAllocateIn in = buildOpenTiXian(params,log);
        AppTiXianParams in = buildOpenTiXian(params,log);
        String sign = Sign.sign(in.generateClearText(), Constants.MCHNT_PRIVATE_KEY);
        in.setSignature(sign);
        String reqWithSign = XmlConvertUtil.bean2Xml(in);
        // 将JSON字符串转换为JSON对象
        // 将XML对象转换为字符串
        System.out.println(reqWithSign);
        JSONObject json = JSONUtil.parseObj(reqWithSign);
        String xml = JSONUtil.toXmlStr(json);
        String xmlWithDeclaration = "<xml>\n" + xml+"</xml>\n";
        Map<String, String> param = new HashMap<String, String>();
        param.put("req", URLEncoder.encode(xmlWithDeclaration, "GBK"));
        String res = HttpUtils.post(Constants.QUERY_BALANCE_ALLOCATE, param);
        res = URLDecoder.decode(res, "GBK");
        System.out.println(res);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 将 XML 字符串解析为 Document 对象
        Document document = builder.parse(new java.io.ByteArrayInputStream(res.getBytes("UTF-8")));
        // 获取根元素
        Element root = document.getDocumentElement();
        String resCode = getNodeValue(root,"respCode");
        if ("0000".equals(resCode)){
            log.setState(1L);
            skWithdrawalLogMapper.updateById(log);
        }else{
            throw new RuntimeException(getNodeValue(root,"respDesc"));
        }
    }
    // 辅助方法：获取节点值
    private static String getNodeValue(Element parent, String nodeName) {
        NodeList nodeList = parent.getElementsByTagName(nodeName);
        if (nodeList != null && nodeList.getLength() > 0) {
            Node node = nodeList.item(0);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                return node.getTextContent();
            }
        }
        return "";
    }


    public static AppTiXianParams buildOpenTiXian(SkAppUser params, SkWithdrawalLog log){
        AppTiXianParams in = new AppTiXianParams();
        in.setTraceNo(log.getId()+"");
        in.setMchntCd(Constants.MCHNT_CD);
        AppTiXianParams.accountIn accountIn = new AppTiXianParams.accountIn();
        accountIn.setAccountIn(params.getAccountIn());
        BigDecimal allMoney = new BigDecimal(100).multiply(log.getPrice());
        accountIn.setAllocateAmt(allMoney.toString());
        accountIn.setAllocateType("01");
        accountIn.setCleanBankNo(params.getKaHao());
        AppTiXianParams.accountIn2 accountInlist = new AppTiXianParams.accountIn2();
        accountInlist.getAccountInlist().add(accountIn);
        in.setAccountInlist(accountInlist);
        return in;
    }

    /**
     * 解绑提现账户
     * @param skAppUser
     */
    public Element delTiXian(SkAppUser skAppUser) {

        AppDelectParam app = buildDelectTiXian(skAppUser);
        try {
            String sign = Sign.sign(app.generateClearText(), Constants.MCHNT_PRIVATE_KEY);
            app.setSignature(sign);
            String reqWithSign = XmlConvertUtil.bean2Xml(app);
            // 将JSON字符串转换为JSON对象
            // 将XML对象转换为字符串
            System.out.println(reqWithSign);
            JSONObject json = JSONUtil.parseObj(reqWithSign);
            String xml = JSONUtil.toXmlStr(json);
            String xmlWithDeclaration = "<xml>\n" + xml+"</xml>\n";
            Map<String, String> param = new HashMap<String, String>();
            param.put("req", URLEncoder.encode(xmlWithDeclaration, "GBK"));
            String res = HttpUtils.post(Constants.INVALID_ALLOCATE_ACCOUNT, param);
            res = URLDecoder.decode(res, "GBK");
            System.out.println(res);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 将 XML 字符串解析为 Document 对象
            Document document = builder.parse(new java.io.ByteArrayInputStream(res.getBytes("UTF-8")));
            // 获取根元素
            Element root = document.getDocumentElement();

            return root;
        }catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    private AppDelectParam buildDelectTiXian(SkAppUser skAppUser){
        AppDelectParam app = new AppDelectParam();
        app.setTraceNo(System.currentTimeMillis() + "");
        app.setMchntCd(Constants.MCHNT_CD);
        app.setAccountIn(skAppUser.getAccountIn());
        return app;
    }

    public void getFuYouCallBack(AppFuYouCallBack params) throws Exception {
        String res = URLDecoder.decode(params.getMessage(), "GBK");
        System.out.println(res);
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 将 XML 字符串解析为 Document 对象
        Document document = builder.parse(new java.io.ByteArrayInputStream(res.getBytes("UTF-8")));
        // 获取根元素
        Element root = document.getDocumentElement();
        String messages = getNodeValue(root,"message");
        System.out.println(messages);
        // 创建JAXB上下文
        JSONObject json = JSONUtil.parseObj(messages);
        JSONObject json2 = JSONUtil.parseObj(json.get("AccountInResultOut"));
        AccountInResultOut out = json2.toBean(AccountInResultOut.class);
        boolean verifySign = Sign.verify(out.generateClearText().getBytes(Constants.CHARSET), Constants.FY_PUBLIC_KEY, out.getSignature());
        System.out.println(out);
        if (!verifySign) {
            throw new RuntimeException("返回报文验签失败");
        }
        if ("0000".equals(out.getStatus())){
            SkAppUser skAppUser = skAppUserMapper.selectOne(
                new QueryWrapper<SkAppUser>().lambda()
                    .eq(SkAppUser::getAccountIn,out.getAccountIn())
                    .last("limit 1")
            );
            skAppUser.setIsFy(1L);
            skAppUserMapper.updateById(skAppUser);
//            skAppUser.setAccountIn("");
//            skAppUser.setHuMing("");
//            skAppUser.setKaHao("");
//            skAppUser.setTxPhone("");
//            skAppUser.setZjNum("");
//            skAppUser.setZjType("");
//            skAppUserMapper.updateById(skAppUser);
        }
    }

    public static void main(String[] args) throws Exception {
        String message = "{accountInlist={subBranchName=, cards={cards={isDefault=01, mobile=17897619394, issInsName=招商银行, interBankNo=, outAcntNo=6214831255555555, status=02, outAcntNm=蔡帅帅}}, licNo=371322199208153875, bankAcctNo=, mobile=17897619394, issInsName=, accountIn=037054220250613500003680299, outAcntNo=6214831255555555, allocateScale=10000, outAcntNm=蔡帅帅, bankInterNo=, depositAccount=, issCityName=, interBankNo=, mchntTraceNo=1749806244957, fyInterBankNo=, bankNm=, status=02}}";
        System.out.println(message);
        BeanItem out = XmlConvertUtil.xml2Bean(message, BeanItem.class);
        System.out.println(out);
    }

    public Element deleteWithdrawalAccount(SkAppUser skAppUser) {
        AppDelectParam app = buildDelectTiXian(skAppUser);

        try {
            String sign = Sign.sign(app.generateClearText(), Constants.MCHNT_PRIVATE_KEY);
            app.setSignature(sign);
            String reqWithSign = XmlConvertUtil.bean2Xml(app);
            // 将JSON字符串转换为JSON对象
            // 将XML对象转换为字符串
            System.out.println(reqWithSign);
            JSONObject json = JSONUtil.parseObj(reqWithSign);
            String xml = JSONUtil.toXmlStr(json);
            String xmlWithDeclaration = "<xml>\n" + xml+"</xml>\n";
            Map<String, String> param = new HashMap<String, String>();
            param.put("req", URLEncoder.encode(xmlWithDeclaration, "GBK"));
            String res = HttpUtils.post(Constants.CLOSE_ACCOUNT, param);
            res = URLDecoder.decode(res, "GBK");
            System.out.println(res);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 将 XML 字符串解析为 Document 对象
            Document document = builder.parse(new java.io.ByteArrayInputStream(res.getBytes("UTF-8")));
            // 获取根元素
            Element root = document.getDocumentElement();
            return root;
        }catch (Exception e) {
            e.printStackTrace();
            return null;
        }


    }
//        BatchTradeAllocateIn.SrcFasSsn ssn1 = new BatchTradeAllocateIn.SrcFasSsn();
//        ssn1.setSrcFasDate("20230225");
//        ssn1.setSrcFasSsn("062031871839");
//        in.getSrcFasSsnList().add(ssn1);

//        BatchTradeAllocateIn.AccountIn account1 = new BatchTradeAllocateIn.AccountIn();
//        account1.setAccountIn(params.getAccountIn());
//        account1.setAllocateAmt(1);
//        in.getAccountInlist().add(account1);
}
